using System;
using System.Collections.Generic;
using System.Text;
using Mpi.Common.Collections;
using System.ComponentModel;
using System.Linq;

namespace Xeml.Document
{
    /// <summary>
    /// The representation of a story wich contains information about parameter and observation points.
    /// </summary>
    public abstract class StoryBase : XemlAnnotatableObject, INotifyPropertyChanged
    {
        #region Fields

        protected string _label;
        protected StoryBoard _storyBoard;
        protected ObservationPointCollection _observationPoints;
        protected VariableCollection _variables;
        protected SampleCollection _samples;
        protected SuperSampleCollection _superSamples;
        private EventCollection _events;

        #endregion Fields

        #region Properties

        /// <summary>
        /// A friendly meaningful label for this story.
        /// </summary>
        public string Label
        {
            get
            {
                return this._label;
            }
            set
            {
                if (_label != value)
                {
                    this._label = value;
                    OnPropertyChanged("Label");
                }
            }
        }


        /// <summary>
        /// The storyboard that is the owner this story.
        /// </summary>
        public StoryBoard StoryBoard
        {
            get { return _storyBoard; }
            set { _storyBoard = value; }
        }


        /// <summary>
        /// A list of measure points.
        /// </summary>
        public ObservationPointCollection ObservationPoints
        {
            get
            {
                return this._observationPoints;
            }
        }

        /// <summary>
        /// A collection of parameter,
        /// </summary>
        public VariableCollection Variables
        {
            get
            {
                return this._variables;
            }
        }


        /// <summary>
        /// A collection  of contained samples.
        /// </summary>
        public SampleCollection Samples
        {
            get
            {
                return this._samples;
            }

        }

        /// <summary>
        /// A collection of super samples
        /// </summary>
        public SuperSampleCollection SuperSamples
        {
            get
            {
                return _superSamples;
            }

        }

        /// <summary>
        /// A collection of events appeared in this story.
        /// </summary>
        public EventCollection Events
        {
            get { return _events; }

        }

        #endregion

        #region Constructors

        /// <summary>
        /// The standard constructor
        /// </summary>
        public StoryBase()
        {
            this._observationPoints = new ObservationPointCollection(this);
            this._variables = new VariableCollection(this);
            this._samples = new SampleCollection(this);
            this._superSamples = new SuperSampleCollection(this);
            this._events = new EventCollection(this);

            this._observationPoints.CollectionChanged += (object sender, EventArgs e) => { OnPropertyChanged("ObservationPoints"); };
            this._variables.CollectionChanged += (object sender, EventArgs e) => {
                OnPropertyChanged("Parameters"); 
            };
            this._samples.CollectionChanged += (object sender, EventArgs e) => { OnPropertyChanged("Samples"); };
            this._superSamples.CollectionChanged += (object sender, EventArgs e) => { OnPropertyChanged("SuperSamples"); };
            this._events.CollectionChanged += (object sender, EventArgs e) => { OnPropertyChanged("Events"); };

            //this._observationPoints.ItemAdded += new EventHandler<ItemEventArgs<ObservationPoint>>(OnObservationAdded);
            //this._observationPoints.ItemRemoved += new EventHandler<ItemEventArgs<ObservationPoint>>(OnObservationRemoved);
            //this._parameters.ItemAdded += new EventHandler<ItemEventArgs<Parameter>>(OnParameterAdded);
            //this._parameters.ItemRemoved += new EventHandler<ItemEventArgs<Parameter>>(OnParameterRemoved);
        }

        #endregion Constructors

        #region Methods

        /// <summary>
        /// Calculates a list of all parameter names and it's context names.
        /// </summary>
        /// <returns>A dictionary of parameter names and it's context names.</returns>
        public Dictionary<string, List<string>> AllOverParameterNames()
        {
            Dictionary<string, List<string>> ret = new Dictionary<string, List<string>>();
            TreeNode<StoryBase> node = this.StoryBoard.FindNode(this); //Find me in the tree.
            if (this is StorySplit)
            {
                Dictionary<string, List<string>> temp = node.Parent.Content.AllOverParameterNames();
                foreach (string paramName in temp.Keys)
                {
                    if (!ret.ContainsKey(paramName))
                    {
                        List<string> props = new List<string>();
                        ret.Add(paramName, props);
                    }
                    foreach (string propName in temp[paramName])
                    {
                        if (!ret[paramName].Contains(propName))
                            ret[paramName].Add(propName);
                    }
                }
            }

            foreach (string paramName in this._variables.ListIdentifiers())
            {
                if (!ret.ContainsKey(paramName))
                {
                    List<string> props = new List<string>();
                    ret.Add(paramName, props);
                }
                ret[paramName].AddRange((this._variables.Find(paramName) as DynamicTerm).ContextList().ToArray());
             }
            return ret;
        }

    


        /// <summary>
        /// Calculates all parameters and it's used context names.
        /// </summary>
        /// <returns>A dictionary of parameter names and it's context names.</returns>
        public Dictionary<DynamicTerm, List<string>> AllOverParameters()
        {
            Dictionary<DynamicTerm, List<string>> ret = new Dictionary<DynamicTerm, List<string>>();
            TreeNode<StoryBase> node = this.StoryBoard.FindNode(this); //Find me in the tree.
            if (this is StorySplit)
            {
                Dictionary<DynamicTerm, List<string>> temp = node.Parent.Content.AllOverParameters();
                foreach (DynamicTerm p in temp.Keys)
                {
                    if (!ret.ContainsKey(p))
                    {
                        List<string> props = new List<string>();
                        ret.Add(p, props);
                    }
                    foreach (string contextName in temp[p])
                    {
                        if (!ret[p].Contains(contextName))
                            ret[p].Add(contextName);
                    }
                }
            }

            foreach (DynamicTerm p in this._variables)
            {
                if (!ret.ContainsKey(p))
                {
                    List<string> contexts = new List<string>();
                    ret.Add(p, contexts);
                }
                ret[p].AddRange(p.ContextList().ToArray());
                //foreach (Property prop in this.parameters[paramName].Properties)
                //{
                //    if (!ret[paramName].Contains(prop.Name))
                //        ret[paramName].Add(prop.Name);
                //}
            }
            return ret;
        }


        /// <summary>
        /// Calculates all the variables which are inherited from the parent stories.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<string> InheritedVariablesTerms()
        {
            //tmpList and index are used to yield distinct list of variableterms
            List<string> tmpList = new List<string>();
            int index;

            TreeNode<StoryBase> node = this.StoryBoard.FindNode(this); //Find me in the tree.
            if (this is StorySplit)
            {
                foreach (string vid in node.Parent.Content.InheritedVariablesTerms())
                {
                    index = tmpList.BinarySearch(vid);
                    if (index < 0)
                    {
                        tmpList.Insert(~index, vid);
                        yield return vid;
                    }
                }
            }
            foreach (DynamicTerm p in this._variables)
            {
                index = tmpList.BinarySearch(p.TermId);
                if (index < 0)
                {
                    tmpList.Insert(~index, p.TermId);
                    yield return p.TermId;
                }
            }
        }

        /// <summary>
        /// Calculates a sorted list of values which a applied in the whole history of this story.
        /// </summary>
        /// <param name="parameterName">The parameter name</param>
        /// <param name="context">The parameter name</param>
        /// <returns>The sorted list of values.</returns>
        public SortedDictionary<TimeSpan, ValueBase> SortedHistoryValues(string parameterName, string context)
        {
            return new SortedDictionary<TimeSpan, ValueBase>(HistoryValues(parameterName, context).ToDictionary(k => k.Key, v => v.Value as ValueBase));
        }

        /// <summary>
        /// Calculates a list of defined values of an property through it's all over history.
        /// </summary>
        /// <param name="parameterName">The parameter name.</param>
        /// <param name="context">The parameter name</param>
        /// <returns>The dictionary of timepoints and values.</returns>
        public Dictionary<TimeSpan, DynamicValueBase> HistoryValues(string parameterName, string context)
        {
            Dictionary<TimeSpan, DynamicValueBase> ret = new Dictionary<TimeSpan, DynamicValueBase>();

            TreeNode<StoryBase> node = this.StoryBoard.FindNode(this); //Find me in the tree.
            if (this is StorySplit)
            {
                StorySplit ss = this as StorySplit;
                Dictionary<TimeSpan, DynamicValueBase> temp;
                temp = node.Parent.Content.HistoryValues(parameterName, context);
                foreach (DynamicValueBase vb in temp.Values.Where(x => x.Time <= ss.TimePoint))
                {
                    ret.Add(vb.Time, vb);
                }
            }

            if (this.Variables.Contains(parameterName))
            {
                DynamicTerm p = this.Variables.Find(parameterName) as DynamicTerm;
                List<DynamicValueBase> vals = p.ValueBy(context);
                foreach (DynamicValueBase vb in vals)
                {
                    if (ret.ContainsKey(vb.Time))
                        ret.Remove(vb.Time); //override value at the same time point
                    ret.Add(vb.Time, vb);
                }
            }
            return ret;
        }


        /// <summary>
        /// List all tim points where the environment is changed whithin this story.
        /// </summary>
        /// <param name="recursive">Indicates whether inherited changes are included.</param>
        /// <returns>The list of time points where something is changed.</returns>
        public IEnumerable<TimeSpan> HistoryTimePoints(bool recursive)
        {
            List<TimeSpan> tmpList = new List<TimeSpan>();
            int index;

            TreeNode<StoryBase> node = this.StoryBoard.FindNode(this); //Find me in the tree.
            if (this is StorySplit && recursive)
            {
                foreach (TimeSpan ts in node.Parent.Content.HistoryTimePoints(recursive))
                {
                    index = tmpList.BinarySearch(ts);
                    if (index < 0)
                    {
                        tmpList.Insert(~index, ts);
                        yield return ts;
                    }
                }
            }

            foreach (DynamicTerm p in this.Variables)
            {
                foreach (DynamicValueBase vb in p.Values)
                {
                    index = tmpList.BinarySearch(vb.Time);
                    if (index < 0)
                    {
                        tmpList.Insert(~index, vb.Time);
                        yield return vb.Time;
                    }
                }
            }
        }
        #endregion

        #region Event-/handling
        //public event EventHandler Changed;
        //public event EventHandler<ItemEventArgs<ObservationPoint>> ObservationPointAdded;
        //public event EventHandler<ItemEventArgs<ObservationPoint>> ObservationPointRemoved;

        //protected virtual void OnChanged()
        //{
        //    if (Changed != null)
        //        Changed(this, new EventArgs());
        //}

        //void OnObservationRemoved(object sender, ItemEventArgs<ObservationPoint> e)
        //{
        //    ObservationPoint op = e.Item as ObservationPoint;

        //    op.Changed -= new EventHandler(OnObservationOrParameterChanged);
        //    if(ObservationPointRemoved != null)
        //        ObservationPointRemoved(this,new ItemEventArgs<ObservationPoint>(op));
        //    OnChanged();
        //}

        //void OnObservationAdded(object sender, ItemEventArgs<ObservationPoint> e)
        //{
        //    System.Diagnostics.Debug.WriteLine("StoryBase: ObPoint added");
        //    ObservationPoint op = e.Item as ObservationPoint;
        //    op.Changed += new EventHandler(OnObservationOrParameterChanged);
        //    if (ObservationPointAdded != null)
        //        ObservationPointAdded(this, new ItemEventArgs<ObservationPoint>(op));

        //    OnChanged();
        //}


        //void OnParameterRemoved(object sender, ItemEventArgs<Parameter> e)
        //{
        //   e.Item.Changed -= new EventHandler(OnObservationOrParameterChanged);
        //    OnChanged();
        //}

        //void OnParameterAdded(object sender, ItemEventArgs<Parameter> e)
        //{
        //    e.Item.Changed += new EventHandler(OnObservationOrParameterChanged);
        //    OnChanged();
        //}


        //void OnObservationOrParameterChanged(object sender, EventArgs e)
        //{
        //    OnChanged();
        //}
        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
        #endregion
    }
}
